/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

#include "stdafx.h"

#include "DConfigGlobals.h"
#include "CImplementer.h"
#include "Interpreter.h"	// Only for fsCreate used in CSymbolImpl::SerializeIn()
#include <string.h>
#include "Base64.h"

///////////////////////////////////
#if defined(USING_MFC) && defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////

CChunkImpl::CChunkImpl(int length, const U8* data, int encoding, int itype)
{
	m_IndexType = itype;
	m_EncodingType = encoding;
	m_Dimensions = NULL;
	m_DimensionCount = 1;

	m_Length = length;
	if (length == 0) m_Data = NULL;
	else {
		m_Data = (U8*)malloc(length);
		if (data == NULL) memset(m_Data, 0, length);
		else memcpy(m_Data, data, length);
	}
}

CChunkImpl::CChunkImpl(TString text, int encoding, int itype)
{
	m_IndexType = itype;
	m_EncodingType = encoding;
	m_Dimensions = NULL;
	m_DimensionCount = 1;

	if (encoding == enc_base64) {
		VALUE vTempChunk;
		if (DecodeBase64(text, vTempChunk)) {
			// Swipe the data from the temp chunk
			m_Length = vTempChunk.Chunk()->m_Length;
			m_Data = vTempChunk.Chunk()->m_Data;
			// Don't let the temp chunk delete the data
			vTempChunk.Chunk()->m_Length = 0;
			vTempChunk.Chunk()->m_Data = NULL;
		}
		else {
			RunTimeError(_T("Base64 decoding failed in chunk constructor"));
		}
	}
	else if (encoding == enc_hex) {
		VALUE vTempChunk;
		if (DecodeHex(text, vTempChunk)) {
			// Swipe the data from the temp chunk
			m_Length = vTempChunk.Chunk()->m_Length;
			m_Data = vTempChunk.Chunk()->m_Data;
			// Don't let the temp chunk delete the data
			vTempChunk.Chunk()->m_Length = 0;
			vTempChunk.Chunk()->m_Data = NULL;
		}
		else {
			RunTimeError(_T("Hex decoding failed in chunk constructor"));
		}
	}
	else if (encoding == enc_ascii) {
		m_Length = text.GetLength();
		if (m_Length == 0) {
			m_Data = NULL;
		}
		else {
			m_Data = (U8*)malloc(m_Length);
			#ifdef UNDER_CE
				for (int idx = 0; idx < m_Length; ++idx) {
					m_Data[idx] = (U8)(text[idx]);
				}
			#else
				memcpy(m_Data, (LPCTSTR)text, m_Length);
			#endif
		}
	}
	else {
		InternalError(_T("Unsupported encoding in chunk constructor"));
	}
}

CChunkImpl::~CChunkImpl()
{
	free(m_Data);
	delete[] m_Dimensions;
}

U8* CChunkImpl::GetData()
{
	return m_Data;
}

U8* CChunkImpl::SerializeIn(U8* inBuffer, CImplementer** result)
{
	// The type byte was already read, so don't look for it
	int length, encodingType, indexType, dimensionCount, *dimensions = NULL;
	inBuffer = SerializeInInt(inBuffer, length);
	inBuffer = SerializeInInt(inBuffer, encodingType);
	inBuffer = SerializeInInt(inBuffer, indexType);
	inBuffer = SerializeInInt(inBuffer, dimensionCount);
	if (dimensionCount > 1) {
		dimensions = new int[dimensionCount];
		for (int idx = 0; idx < dimensionCount; ++idx) {
			inBuffer = SerializeInInt(inBuffer, dimensions[idx]);
		}
	}

	CChunkImpl* chunk = new CChunkImpl(length);
	if (length > 0) {
		memcpy(chunk->m_Data, inBuffer, length);
		inBuffer += length;
	}
	chunk->m_EncodingType = encodingType;
	chunk->m_IndexType = indexType;
	chunk->m_DimensionCount = dimensionCount;
	chunk->m_Dimensions = dimensions;
	*result = chunk;
	return inBuffer;
}

void CChunkImpl::SerializeOut(CFile* outFile)
{
	U8 header = ST_CHUNK;
	outFile->Write(&header, 1);
	SerializeOutInt(outFile, m_Length);
	SerializeOutInt(outFile, m_EncodingType);
	SerializeOutInt(outFile, m_IndexType);
	SerializeOutInt(outFile, m_DimensionCount);
	if (m_DimensionCount > 1) {
		for (int idx = 0; idx < m_DimensionCount; ++idx) {
			SerializeOutInt(outFile, m_Dimensions[idx]);
		}
	}
	if (m_Length > 0) {
		outFile->Write(m_Data, m_Length);
	}
}

int CChunkImpl::__length__() const
{
	// Of course, we are assuming the length is an exact multiple here!
	int result;
	switch (m_IndexType) {
		case it_i1:	case it_ui1:				result = m_Length;		break;
		case it_i2:	case it_ui2:				result = m_Length >> 1;	break;
		case it_i4:	case it_ui4: case it_f4:	result = m_Length >> 2;	break;
		case it_i8:	case it_ui8: case it_f8:	result = m_Length >> 3;	break;
		case it_ui3:							result = m_Length / 3;	break;
	}
	return result;
}

void CChunkImpl::__resize__(int newLength, VALUE init)
{
	// Determine how many bytes the newLength requires
	// Remember that m_Length is always in bytes
	switch (m_IndexType) {
		case it_i1:	case it_ui1:				/*newLength = newLength;*/	break;
		case it_i2:	case it_ui2:				newLength = newLength << 1;	break;
		case it_i4:	case it_ui4: case it_f4:	newLength = newLength << 2;	break;
		case it_i8:	case it_ui8: case it_f8:	newLength = newLength << 3;	break;
		case it_ui3:							newLength = newLength * 3;	break;
		default:
			RunTimeError(_T("Unsupported itype in chunk.__resize__()"));
			return;
	}

	if (newLength < 0) {
		RunTimeError(_T("Length less than zero in chunk.__resize__()"));
	}
	else if (newLength != m_Length) {
		m_Data = (U8*)realloc(m_Data, newLength);
		if ((m_Data == NULL) && (newLength > 0)) {
			RunTimeError(_T("Failed to realloc chunk memory"));
		}
		else {
			int diff = newLength - m_Length;
			if (diff > 0) {
				memset(m_Data + m_Length, 0, diff); // Need to add zeros to end
			}
			m_Length = newLength;
		}
	}
}

TString CChunkImpl::__unparse__()
{
	TString result = _T("[:{encoding:\"");
	switch (m_EncodingType) {
		case enc_base64:	result += _T("base64"); break;
		case enc_hex:		result += _T("hex");	break;
		case enc_binary:	result += _T("binary"); break;
		case enc_ascii:		result += _T("ascii"); break;
	}
	result += _T("\", itype:\"");
	switch (m_IndexType) {
		case it_i1:		result += _T("i1");		break;
		case it_i2:		result += _T("i2");		break;
		case it_i4:		result += _T("i4");		break;
		case it_i8:		result += _T("i8");		break;
		case it_ui1:	result += _T("ui1");	break;
		case it_ui2:	result += _T("ui2");	break;
		case it_ui3:	result += _T("ui3");	break;
		case it_ui4:	result += _T("ui4");	break;
		case it_ui8:	result += _T("ui8");	break;
		case it_f4:		result += _T("f4");		break;
		case it_f8:		result += _T("f8");		break;
	}
	return result + _T("\"}, \"") + __string__() + _T("\":]");
}

TString CChunkImpl::__string__()
{
	TString result;
	if (m_Length != 0) {
		if (m_EncodingType == enc_base64) {
			if (!EncodeBase64(m_Data, m_Length, result)) {
				RunTimeError(_T("Base64 encoding of chunk failed"));
			}
		}
		else if (m_EncodingType == enc_hex) {
			if (!EncodeHex(m_Data, m_Length, result)) {
				RunTimeError(_T("Hex encoding of chunk failed"));
			}
		}
		else if (m_EncodingType == enc_ascii) {
			LPTSTR buf = result.GetBuffer(m_Length);
			#ifdef UNDER_CE
				for (int idx = 0; idx < m_Length; ++idx) {
					buf[idx] = (TCHAR)(m_Data[idx]);
				}
			#else
				memcpy(buf, m_Data, m_Length);
			#endif
			result.ReleaseBuffer(m_Length);
		}
		else {
			InternalError(_T("Unsupported chunk encoding type"));
		}
	}
	return result;
}

VALUE CChunkImpl::__clone__()
{
	VALUE vNewChunk = AllocChunk(m_Length, m_Data);
	CChunkImpl *pChunk = vNewChunk.Chunk();
	pChunk->m_EncodingType = m_EncodingType;
	pChunk->m_IndexType = m_IndexType;
	pChunk->m_DimensionCount = m_DimensionCount;
	if (m_DimensionCount > 1) {
		pChunk->m_Dimensions = new int[m_DimensionCount];
		for (int idx = 0; idx < m_DimensionCount; ++idx) {
			pChunk->m_Dimensions[idx] = m_Dimensions[idx];
		}
	}
	else {
		pChunk->m_Dimensions = NULL;
	}
	return vNewChunk;
}

VALUE CChunkImpl::__deepclone__()
{
	return __clone__();
}

VALUE CChunkImpl::__get__(const VALUE& arg)
{
	if (arg.Type() != stInteger) {
		RunTimeError(_T("Invalid index type for chunk: %s"), (LPCTSTR)arg.TypeString());
	}
	else {
		int idx = arg.Integer();
		if ((m_Length != 0) && (idx <= __length__()) && (idx >= 1)) {
			--idx;
			switch (m_IndexType) {
				case it_i1:		return (int)((char*)m_Data)[idx];
				case it_i2:		return (int)((short*)m_Data)[idx];
				case it_i4:		return (int)((int*)m_Data)[idx];
				case it_ui1:	return (int)((unsigned char*)m_Data)[idx];
				case it_ui2:	return (int)((unsigned short*)m_Data)[idx];
				case it_f4:		return (float)((float*)m_Data)[idx];
				default:
					ASSERT(false);
					RunTimeError(_T("Unsupported itype in get in chunk"));
			}
		}
	}
	return AllocNil();
}

void CChunkImpl::__set__(const VALUE& indexer, const VALUE& arg)
{
	if (indexer.Type() != stInteger) {
		RunTimeError(_T("Invalid index type for chunk: %s"), (LPCTSTR)indexer.TypeString());
		return;
	}

	int idx = indexer.Integer();
	if ((m_Length != 0) && (idx <= __length__()) && (idx >= 1)) {
		--idx;
		switch (m_IndexType) {
			case it_i1:		((char*)m_Data)[idx] = (char)arg.Integer();		break;
			case it_i2:		((short*)m_Data)[idx] = (short)arg.Integer();	break;
			case it_i4:		((int*)m_Data)[idx] = (int)arg.Integer();		break;
			case it_ui1:	((U8*)m_Data)[idx] = (U8)arg.Integer();			break;
			case it_ui2:	((U16*)m_Data)[idx] = (U16)arg.Integer();		break;
			case it_f4:		((float*)m_Data)[idx] = arg.Float();			break;
			default:
				ASSERT(false);
				RunTimeError(_T("Unsupported itype in set in chunk"));
		}
	}
}

void CChunkImpl::__findnext__(FindNextInfo& fni, VALUE& key)
{
	if (fni.info == FIND_NEXT_START) {
		fni.data0 = 1;
		fni.data1 = __length__();
		fni.info = FIND_NEXT_INPROG;
	}
	else if (fni.info != FIND_NEXT_INPROG) {
		InternalError(_T("Interpreter expecting FIND_NEXT_INPROG in __findnext__"));
	}

	if (fni.data0 > fni.data1) {
		fni.info = FIND_NEXT_FINISHED;
	}
	else {
		key = __get__(fni.data0++);
	}
}

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////

CFunctionImpl::CFunctionImpl()
:m_CodeStream(NULL), m_ParamCount(0), m_ParamList(NULL),
	m_pClassImpl(NULL), m_pClassInstImpl(NULL), m_EndLine(-1){}

CFunctionImpl::CFunctionImpl(CByteCode* code, TString paramList[], int paramCount)
:m_CodeStream(code), m_ParamCount(paramCount),
	m_pClassImpl(NULL), m_pClassInstImpl(NULL), m_EndLine(-1)
{
	// A m_ParamCount of -1 means this is a core function (C/C++ code)
	// In which case, when executed, we'll take care of making sure it
	// receives the correct number of args.
	// m_ParamCount of zero means a normal interpreted function with no args

	// NOTE - I don't think this constructor will ever be called with paramCount == -1
	// since we now have the core version constructor

	if (m_ParamCount > 0) {
		m_ParamList = new TString[m_ParamCount];
		for (int idx = 0; idx < m_ParamCount; idx++) {
			m_ParamList[idx] = paramList[idx];
		}
		m_ModuleName = code->ModuleName();
	}
	else {
		m_ParamList = NULL;
	}
}

CFunctionImpl::CFunctionImpl(VALUE (*lpCoreFunc)(VALUE* self, int argCount, VALUE argVals[],
							 CInterpreter* pInterp), TString moduleName)
:m_lpCoreFunc(lpCoreFunc), m_ParamCount(-1), m_ParamList(NULL),
	m_pClassImpl(NULL), m_pClassInstImpl(NULL), m_EndLine(-1)
{
	m_ModuleName = moduleName;
}

CFunctionImpl::~CFunctionImpl()
{
	if (m_ParamCount != -1) {
		// When param count is -1, the function is a C core function pointed
		// to in the union as m_lpCoreFunc.  We better not try to delete hard code!
		delete m_CodeStream;
	}
	delete[] m_ParamList;
}

TString CFunctionImpl::__string__()
{
	return __unparse__();
}

TString CFunctionImpl::__unparse__()
{
	if (m_ParamCount > -1) {
		if (m_CodeStream != NULL) {
			TString result = _T("func(");
			for (int idx = 0; idx < m_ParamCount; ++idx) {
				result += m_ParamList[idx];
				if (idx < m_ParamCount - 1) result += _T(", ");
			}
			result += _T(")\r\n");
			result += m_CodeStream->__unparse__();
			if (result[result.GetLength() - 1] != '\n') result += "\r\n";
			result += _T("endfunc\r\n");
			return result;
		}
		return _T("<empty_function>");
	}
	else {
		return _T("<core_function>");
	}
}

VALUE CFunctionImpl::__clone__()
{
	if (m_ParamCount >= 0) {
		CByteCode* code = new CByteCode(*m_CodeStream);
		return AllocFunction(code, m_ParamList, m_ParamCount);
	}
	else {
		return AllocFunction(m_lpCoreFunc, m_ModuleName);
	}
}

VALUE CFunctionImpl::__deepclone__()
{
	return __clone__();
}

U8* CFunctionImpl::SerializeIn(U8* inBuffer, CImplementer** result)
{
	// The type byte was already read, so don't look for it
	CFunctionImpl* func = new CFunctionImpl();

	int paramCount = *(inBuffer++);
	func->m_ParamCount = paramCount;
	func->m_ParamList = new TString[paramCount];

	for (int idx = 0; idx < paramCount; ++idx) {
		// Now read in the arrays contents
		inBuffer = SerializeInTString(inBuffer, func->m_ParamList[idx]);
	}

	// Read in the compiled codestream
	inBuffer = CByteCode::SerializeIn(inBuffer, &(func->m_CodeStream));

	// Somewhat redundant...  Shouldn't really be necesary, just precausion...
	func->m_ModuleName = func->m_CodeStream->ModuleName();

	*result = func;

	return inBuffer;
}

void CFunctionImpl::SerializeOut(CFile* outFile)
{
	if (m_ParamCount == -1) {
		RunTimeError(_T("Core functions can't be serialized"));
	}
	else {
		char header[2] = {ST_FUNCTION, (char)m_ParamCount};
		outFile->Write(header, 2);

		for (int idx = 0; idx < m_ParamCount; ++idx) {
			SerializeOutTString(outFile, m_ParamList[idx]);
		}
		m_CodeStream->SerializeOut(outFile);
	}
}

bool CFunctionImpl::ExecuteCoreFunc(VALUE& result, int argCount, VALUE argVals[], CInterpreter* pInterp)
{
	ASSERT(m_ParamCount == -1);	// Core functions must have -1 m_ParamCount

	if (m_pClassInstImpl != NULL) {
		VALUE forcedClassInst;
		forcedClassInst.ForceCI(stClassInstance, m_pClassInstImpl);
		result = (*m_lpCoreFunc)(&forcedClassInst, argCount, argVals, pInterp);
	}
	else {
		result = (*m_lpCoreFunc)(NULL, argCount, argVals, pInterp);
	}

	// Eventually need to change m_lpCoreFunc to return bool for EH
	return (pInterp->GetException().Type() != stClassInstance);
}

//=========================================================

CPropertyImpl::CPropertyImpl(){ASSERT(false);}

CPropertyImpl::CPropertyImpl(VALUE readFunc, VALUE writeFunc, VALUE vInitial)
{
	m_vPropValue = vInitial;

	// Param counts of -1 mean it's a core function and paramcount is deault with there
	if (readFunc.Type() == stFunction) {
		if ((readFunc.Function()->m_ParamCount == 1) || (readFunc.Function()->m_ParamCount == -1)) {
			m_ReadFunc = readFunc;
		}
		else SyntaxError(_T("A properties read function must take exactly one argument"));
	}
	else if (readFunc.Type() == stNil) {
		m_ReadFunc = readFunc;
	}
	else {
		SyntaxError(_T("Invalid property read function, must be of type function or nil"));
	}

	if (writeFunc.Type() == stFunction) {
		if ((writeFunc.Function()->m_ParamCount == 2) || (writeFunc.Function()->m_ParamCount == -1)) {
			m_WriteFunc = writeFunc;
		}
		else SyntaxError(_T("A properties write function must take exactly two arguments"));
	}
	else if (writeFunc.Type() == stNil) {
		m_WriteFunc = writeFunc;
	}
	else {
		SyntaxError(_T("Invalid property write function, must be of type function or nil"));
	}
}

CPropertyImpl::~CPropertyImpl(){}

TString CPropertyImpl::__unparse__()
{
	return m_vPropValue.__unparse__();	// Is this what we want to do here?
}

//=========================================================

CSymbolImpl::CSymbolImpl(TString symName)
:m_SymbolName(symName){};

CSymbolImpl::~CSymbolImpl(){};

TString CSymbolImpl::__unparse__()
{
	TString result;
	result.Format(_T("'%s"), (LPCTSTR)m_SymbolName);
	return result;
}

bool CSymbolImpl::__boolean__()
{
	return true;
}

TString CSymbolImpl::__string__()
{
	return m_SymbolName;
}

VALUE CSymbolImpl::__clone__()
{
	return AllocSymbol(m_SymbolName);
}

VALUE CSymbolImpl::__deepclone__()
{
	return AllocSymbol(m_SymbolName);
}

U8* CSymbolImpl::SerializeIn(U8* inBuffer, CImplementer** result)
{
	TString symbolName;

	inBuffer = SerializeInTString(inBuffer, symbolName);
	*result = new CSymbolImpl(symbolName);

	return inBuffer;
}

void CSymbolImpl::SerializeOut(CFile* outFile)
{
	char header[1] = {ST_SYMBOL};
	outFile->Write(header, 1);
	SerializeOutTString(outFile, m_SymbolName);
}

//=========================================================

CImplementer::CImplementer()
{
	MUTEX_INIT(m_RefMutex);
	m_RefCount = 1;		// Does this need to be surrounded by MUTEX_LOCK?
}

CImplementer::~CImplementer()
{
	MUTEX_DESTROY(m_RefMutex);
}

int CImplementer::IncRefCount()
{
	MUTEX_LOCK(m_RefMutex);
	++m_RefCount;
	MUTEX_UNLOCK(m_RefMutex);
	return m_RefCount;
}

int CImplementer::DecRefCount()
{
	MUTEX_LOCK(m_RefMutex);
	--m_RefCount;
	MUTEX_UNLOCK(m_RefMutex);
	return m_RefCount;
}

void CImplementer::SetRefCount(int refCount)
{
	MUTEX_LOCK(m_RefMutex);
	m_RefCount = refCount;
	MUTEX_UNLOCK(m_RefMutex);
}

void CImplementer::SerializeOut(CFile* outFile){ThrowError(_T("SerializeOut"));};
VALUE* CImplementer::FindValuePtr(const VALUE& key, bool bCreateOK){ThrowError(_T("FindValuePtr"));return NULL;};
void CImplementer::ThrowError(LPCTSTR what) const{RunTimeError(_T("Unimplemented function (%s) "), what);}
bool CImplementer::__boolean__(){ThrowError(_T("__boolean__")); return false;};
TString CImplementer::__unparse__(){ThrowError(_T("__unparse__"));return _T("");};
TString CImplementer::__string__(){ThrowError(_T("__string__")); return _T("");}
void CImplementer::__set__(const VALUE& indexer, const VALUE& arg){ThrowError(_T("__set__"));}
VALUE CImplementer::__get__(const VALUE& arg){ThrowError(_T("__get__")); return VALUE();}
VALUE CImplementer::operator[] (const VALUE& arg){ThrowError(_T("[]")); return VALUE();}
void CImplementer::__findnext__(FindNextInfo& fni, VALUE& value){ThrowError(_T("__findnext__"));};
void CImplementer::__findnext__(FindNextInfo& fni, VALUE& key, VALUE& value){ThrowError(_T("__findnext__"));};
int CImplementer::__length__() const{ThrowError(_T("__length__")); return 0;}
void CImplementer::__resize__(int newLength, VALUE init){ThrowError(_T("__resize__"));}
VALUE CImplementer::__clone__(){ThrowError(_T("__clone__")); return AllocNil();}
VALUE CImplementer::__deepclone__(){
	ThrowError(_T("__deepclone__"));
	return AllocNil();
}

//=========================================================

CArrayImpl::CArrayImpl(int length, const VALUE initEntries[])
{
	// Create the array, but all values are stUndefined
	m_Length = length;
	if (length <= 0) {
		m_Length = 0;	// Could have been negative. force to zero
		m_Values = NULL;
	}
	else {
		m_Values = new VALUE[length];
		if (initEntries != NULL) {
			while (length > 0) {
				length--;
				m_Values[length] = initEntries[length];
			}
		}
	}
}

CArrayImpl::~CArrayImpl()
{
	delete[] m_Values;
}

bool CArrayImpl::__boolean__()
{
	return true;
}

VALUE CArrayImpl::__clone__()
{
	VALUE vResult = AllocArray(m_Length);
	for (int idx = 0; idx < m_Length; ++idx) {
		vResult.Array()->m_Values[idx] = m_Values[idx];
	}
	return vResult;
}

VALUE CArrayImpl::__deepclone__()
{
	VALUE vResult = AllocArray(m_Length);
	for (int idx = 0; idx < m_Length; ++idx) {
		vResult.Array()->m_Values[idx] = m_Values[idx].DeepClone();
	}
	return vResult;
}

void CArrayImpl::__resize__(int newLength, VALUE init)
{
	VALUE vInitializer = init.Clone();

	int idx;

	if (newLength == m_Length) return;
	else if (newLength <= 0)  {
		m_Length = 0;
		if (m_Values != NULL) {
			delete[] m_Values;
			m_Values = NULL;
		}
	}
	else if (newLength > m_Length) {
		// Make the array bigger
		VALUE* newArray = new VALUE[newLength];

		for (idx = 0; idx < m_Length; ++idx) {
			newArray[idx] = m_Values[idx];
		}
		for (idx = m_Length; idx < newLength; ++idx) {
			newArray[idx] = vInitializer;
		}

		delete[] m_Values;
		m_Values = newArray;
		m_Length = newLength;
	}
	else {
		// Make the array smaller
		VALUE* newArray = new VALUE[newLength];

		for (idx = 0; idx < newLength; ++idx) {
			newArray[idx] = m_Values[idx];
		}

		delete[] m_Values;
		m_Values = newArray;
		m_Length = newLength;
	}
}

int CArrayImpl::__length__() const
{
	return m_Length;
}

U8* CArrayImpl::SerializeIn(U8* inBuffer, CImplementer** result)
{
	// The type byte was already read, so don't look for it
	int elementCount;
	inBuffer = SerializeInInt(inBuffer, elementCount);

	*result = new CArrayImpl(elementCount);
	for (int idx = 0; idx < elementCount; ++idx) {
		// Now read in the arrays contents
		inBuffer = ((CArrayImpl*)(*result))->m_Values[idx].SerializeIn(inBuffer);
	}
	return inBuffer;
}

void CArrayImpl::SerializeOut(CFile* outFile)
{
	U8 header = ST_ARRAY;
	outFile->Write(&header, 1);
	SerializeOutInt(outFile, m_Length);
	for (int idx = 0; idx < m_Length; ++idx) {
		m_Values[idx].SerializeOut(outFile);
	}
}

VALUE CArrayImpl::Left(int count)
{
	// Non-destructive, returns new list
	return AllocArray(count, m_Values);
}

VALUE CArrayImpl::Right(int count)
{
	// Non-destructive, returns new list
	// Returns the rightmost count number of entries
	return AllocArray(count, m_Values + (m_Length - count));
}

VALUE CArrayImpl::operator[] (const VALUE& arg)
{
	if (arg.Type() == stInteger) {
		int idx = arg.Integer() - 1;		// AxBasic index starts at one
		if ((idx >= m_Length) || (idx < 0)) {
			//RunTimeError(_T("Array index out of bounds: %d"), idx + 1);
			return AllocNil();
		}
		else return m_Values[idx];
	}
	else {
		InternalError(_T("Invalid array indexer type: %s"), (LPCTSTR)arg.TypeString());
	}
	return VALUE();
}

VALUE g_NilValue = AllocNil();
VALUE* CArrayImpl::FindValuePtr(const VALUE& key, bool bCreateOK)
{
	if (key.Type() == stInteger) {
		int idx = key.Integer() - 1;		// AxBasic index starts at one
		if ((idx >= m_Length) || (idx < 0)) {
			//RunTimeError(_T("Array index out of bounds: %d"), idx + 1);
			g_NilValue = AllocNil();
			return &g_NilValue;	// This could be dangerous
		}
		else return &m_Values[idx];
	}
	else {
		InternalError(_T("Invalid array indexer type: %s"), (LPCTSTR)key.TypeString());
	}
	return NULL;
}

VALUE CArrayImpl::__get__(const VALUE& arg)
{
	if (arg.Type() == stInteger) {
		int idx = arg.Integer() - 1;
		if (idx < 0) {
			idx = m_Length + idx + 1;
		}

		if ((idx >= m_Length) || (idx < 0)) return AllocNil();
		else return m_Values[idx];
	}
	else if (arg.Type() == stArray) {
		// This array means we are returning a range, a new array
		CArrayImpl* pa = arg.Array();
		if ((pa->__length__() != 2) || (pa->m_Values[0].Type() != stInteger) ||
			(pa->m_Values[1].Type() != stInteger))
		{
			RunTimeError(_T("Invalid range indexer used on array"));
		}

		int startIdx = pa->m_Values[0].Integer();
		int endIdx = pa->m_Values[1].Integer();

		if (startIdx < 0) startIdx = m_Length + startIdx + 1;
		if (endIdx < 0) endIdx = m_Length + endIdx + 1;
		if (startIdx < 1) startIdx = 1;
		if (endIdx > m_Length) endIdx = m_Length;

		int size = endIdx - startIdx + 1;
		if (size < 0) {
			RunTimeError(_T("Start index of range occurs after end index"));
		}
		VALUE vResult = AllocArray(size);
		for (int idx = 0; idx < size; ++idx) {
			vResult.Set(idx + 1, m_Values[startIdx + idx - 1]);
		}
		return vResult;
	}
	SyntaxError(_T("Invalid array indexing type: %s"), (LPCTSTR)arg.TypeString());
	return VALUE();
}

void CArrayImpl::__findnext__(FindNextInfo& fni, VALUE& key)
{
	if (fni.info == FIND_NEXT_START) {
		fni.data0 = 0;
		fni.info = FIND_NEXT_INPROG;
	}
	else if (fni.info != FIND_NEXT_INPROG) {
		InternalError(_T("Interpreter expecting FIND_NEXT_INPROG in __findnext__"));
	}

	if (fni.data0 >= m_Length) {
		fni.info = FIND_NEXT_FINISHED;
	}
	else {
		key = m_Values[fni.data0++];
	}
}

void CArrayImpl::__set__(const VALUE& indexer, const VALUE& arg)
{
	if (indexer.Type() == stInteger) {
		int idx = indexer.Integer() - 1;		// Dialect index starts at one
		if ((idx >= m_Length) || (idx < 0)) {
			RunTimeError(_T("Array index out of bounds: %d"), idx + 1);
		}
		else m_Values[idx] = arg;
	}
	else if (indexer.Type() == stArray) {
		CArrayImpl* idxlist = indexer.Array();

		if (idxlist->m_Values[0].Type() != stInteger) {
			RunTimeError(_T("Non-integer value used to index a array."));
		}
		int index = idxlist->m_Values[0].Integer();
		index--;	// Remember OBasic indexes from one, not zero

		if (idxlist->m_Length > 1) {
			m_Values[index].Set(idxlist->Right(idxlist->m_Length - 1), arg);
		}
		else {
			if (index >= m_Length) {	// Don't currently support self-growing arrays
				RunTimeError(_T("Index into array out of bounds"));
			}
			else {
				m_Values[index] = arg;
			}
		}
	}
	else {
		SyntaxError(_T("Invalid array indexing type"));
	}
}

TString CArrayImpl::__string__()
{
	return __unparse__();	// No different for arrays
}

TString CArrayImpl::__unparse__()
{
	static TString tab = _T("    ");
	int startOfLine = 0;
	TString result;
	result = _T("[");
	for (int idx = 0; idx < m_Length; idx++) {
		//if (m_Values[idx].Type() == stArray) {
		//	tab += _T("  ");
		//}

		if ((m_Values[idx].Type() == stArray) || (result.GetLength() - startOfLine > 85)) {
			result += _T("\r\n") + tab;	// Linefeed/tab sequence every 85 chars or so
			startOfLine = result.GetLength() - tab.GetLength();
		}
		result += m_Values[idx].__unparse__();
		if (idx < m_Length - 1) result += _T(", ");

		//if (m_Values[idx].Type() == stArray) {
		//	tab = tab.Left(tab.GetLength() - 2);
		//}
	}
	result += _T("]");
	return result;
}

//=========================================================

CFrameImpl::CFrameLinkNode::CFrameLinkNode(TString key, VALUE value, CFrameLinkNode* next)
:m_Key(key), m_Value(value), m_Next(next){}

CFrameImpl::CFrameLinkNode::~CFrameLinkNode()
{
	/*
	 *	Every link node deletes the following in the chain...
	 */
	delete m_Next;
}

//=========

CFrameImpl::CFrameImpl(int tableSize)
{
	m_NumberOfEntries = 0;
	m_TableSize = tableSize;
	m_HashTable = new CFrameLinkNode*[tableSize];
	for(int idx = 0; idx < tableSize; idx++) {
		m_HashTable[idx] = NULL;
	}
}

CFrameImpl::~CFrameImpl()
{
	for(int idx = 0; idx < m_TableSize; idx++) {
		delete m_HashTable[idx];
	}
	delete[] m_HashTable;
}

bool CFrameImpl::__boolean__()
{
	return true;
}

int CFrameImpl::__length__() const
{
	return m_NumberOfEntries;
}

VALUE CFrameImpl::__clone__()
{
	VALUE vResult = AllocFrame(m_TableSize);
	FindNextInfo fni;
	VALUE key, value;
	while (true) {
		__findnext__(fni, key, value);
		if (fni.info == FIND_NEXT_FINISHED) break;
		vResult.Frame()->__set__(key, value);
	}
	return vResult;
}

VALUE CFrameImpl::__deepclone__()
{
	VALUE vResult = AllocFrame(m_TableSize);
	FindNextInfo fni;
	VALUE key, value;
	while (true) {
		__findnext__(fni, key, value);
		if (fni.info == FIND_NEXT_FINISHED) break;
		vResult.Frame()->__set__(key, value.DeepClone());
	}
	return vResult;
}

VALUE* CFrameImpl::FindValuePtr(TString& key, bool bCreateOK)
{
	return FindValuePtr(VALUE(stString, key), bCreateOK);
}

VALUE* CFrameImpl::FindValuePtr(const VALUE& key, bool bCreateOK)
{
	if (key.Type() == stString) {
		TString string(key.String());
		CFrameImpl::CFrameLinkNode* node = GetNodePtr(string);
		if (node != NULL) {
			return &node->m_Value;
		}
		else if (bCreateOK) {
			node = Add(string, VALUE());
			return &node->m_Value;
		}
		else return NULL;
	}
	InternalError(_T("Invalid arg type sent to CFrameImpl::FindValuePtr()"));
	return NULL;
}

U8* CFrameImpl::SerializeIn(U8* inBuffer, CImplementer** result)
{
	// The type byte was already read, so don't look for it
	VALUE key;
	VALUE value;

	*result = new CFrameImpl(97);

	while (*inBuffer != ST_FRAME_END) {
		inBuffer = key.SerializeIn(inBuffer);
		ASSERT(key.Type() == stString);

		value.StringValue.Empty();	// Just to clear things out from the last loop
		inBuffer = value.SerializeIn(inBuffer);

		((CFrameImpl*)(*result))->Add(key.String(), value);
	}
	++inBuffer;	// Eat ST_FRAME_END

	return inBuffer;
}

void CFrameImpl::SerializeOut(CFile* outFile)
{
	char header[1] = {ST_FRAME};
	outFile->Write(header, 1);

	VALUE key, value;
	FindNextInfo fni;
	while (true) {
		// Update variable
		__findnext__(fni, key, value);

		if (fni.info == FIND_NEXT_FINISHED) break;	// Nothing left

		if (value.Type() != stNil) {
			key.SerializeOut(outFile);
			value.SerializeOut(outFile);
		}
	}

	header[0] = ST_FRAME_END;	// So we know to stop reading key, value pairs
	outFile->Write(header, 1);
}

void CFrameImpl::Empty()
{
	/*
	 *	Delete everything in Frame
	 */
	for (int idx = 0; idx < m_TableSize; idx++) {
		delete m_HashTable[idx];
		m_HashTable[idx] = NULL;
	}
}

CFrameImpl::CFrameLinkNode* CFrameImpl::Add(const TString& key, VALUE value)
{
	/*
	 *	Create and insert a new node at the beginning of it's hash-link-chain
	 */
	int idx = HashFunction(key);
	CFrameLinkNode* node = m_HashTable[idx];
	while (node != NULL) {
		if (node->m_Key.CompareNoCase(key) == 0) {
			break;
		}
		node = node->m_Next;
	}

	if (node == NULL) {
		// Create new node link
		m_NumberOfEntries++;
		node = new CFrameLinkNode(key, value, m_HashTable[idx]);
		m_HashTable[idx] = node;
	}
	else {
		// This key is already present, so just use it's node and replace value
		node->m_Value = value;
	}
	return node;
}

bool CFrameImpl::Remove(const TString& key)
{
	// If the node exists, delete it and return true, else return false
	int idx = HashFunction(key);
	CFrameLinkNode* prev = NULL;
	CFrameLinkNode* node = m_HashTable[idx];
	while (node != NULL) {
		if (node->m_Key.CompareNoCase(key) == 0) {
			if (prev == NULL) {
				m_HashTable[idx] = node->m_Next;
			}
			else {
				prev->m_Next = node->m_Next;
			}
			node->m_Next = NULL;	// Just to be safe, the desctructor might delete m_Next
			delete node;
			m_NumberOfEntries--;
			return true;
		}
		prev = node;
		node = node->m_Next;
	}
	return false;
}

int CFrameImpl::HashFunction(const TString& key) const
{
	/*
	 *	This could be done more efficiently....
	 * 	Produces a hash number from 0 to tableSize - 1.
	 */
	int result = 1;
	TCHAR* data = (TCHAR*)(LPCTSTR)key;
	int length = key.GetLength();

	for (int idx = 0; idx < length; idx++) {
		result = (result << 4) + (data[idx] & 0xFFDF);	// The & makes for case insensitive
	}
	return result >= 0 ? result % m_TableSize : -result % m_TableSize;
}

void CFrameImpl::__set__(const VALUE& indexer, const VALUE& arg)
{
	if (indexer.Type() == stString) {
		Add(indexer.String(), arg);
	}
	else {
		RunTimeError(_T("Index to a frame must be a string value."));
	}
}

VALUE CFrameImpl::__get__(const VALUE& arg)
{
	if (arg.Type() == stString) {
		TString key(arg.String());
		int idx = HashFunction(key);
		CFrameLinkNode* node = m_HashTable[idx];
		while (node != NULL) {
			if (node->m_Key.CompareNoCase(key) == 0) {
				return node->m_Value;
			}
			node = node->m_Next;
		}
		return VALUE();
	}
	else {
		RunTimeError(_T("Index to a frame must be a string value."));
	}
	return VALUE();
}

VALUE CFrameImpl::operator[] (const VALUE& arg)
{
	if (arg.Type() == stString) {
		TString key = arg.String();
		int idx = HashFunction(key);

		CFrameLinkNode* node = m_HashTable[idx];
		while (node != NULL) {
			if (node->m_Key.CompareNoCase(key) == 0) {
				return node->m_Value;
			}
			node = node->m_Next;
		}
		return VALUE();	// Not found, return empty VALUE
	}
	else {
		InternalError(_T("Invalid arg type sent to CFrameImpl::operator[]"));
		return VALUE();
	}
}

TString CFrameImpl::__string__()
{
	return __unparse__();	// No difference with stFrame
}

//#ifndef my__iscsymf
#define my__iscsymf(x) ((((x) >= 'A') && ((x) <= 'Z')) || (((x) >= 'a') && ((x) <= 'z')) || ((x) == '_'))
//#endif
TString CFrameImpl::__unparse__()
{
	TString result;

	result = _T("{\r\n");
	CFrameLinkNode* current;
	for (int idx = 0; idx < m_TableSize; idx++) {
		current = m_HashTable[idx];
		while (current != NULL) {
			if (current->m_Value.Type() != stNil) {
				// if the key contains a space or starts with a digit, wrap the key in double quotes
				bool bUseQuotes = false;
				TCHAR ch;
				LPCTSTR p = current->m_Key;
				int length = current->m_Key.GetLength();
				while ((length-- > 0) && !bUseQuotes) {
					ch = p[length];
					bUseQuotes = !my__iscsymf(ch) && ((length == 0) || ((ch < '0') || (ch > '9')));
				}

				if (bUseQuotes) {
					result += _T("\"");
					result += current->m_Key + _T("\":") + current->m_Value.__unparse__() + _T(",\r\n");
				}
				else {
					result += current->m_Key + _T(":") + current->m_Value.__unparse__() + _T(",\r\n");
				}
			}
			current = current->m_Next;
		}
	}
	result += _T("}");
	return result;
}

void CFrameImpl::__findnext__(FindNextInfo& fni, VALUE& key, VALUE& value)
{
	if (fni.info == FIND_NEXT_START) {
		fni.info = FIND_NEXT_INPROG;
		fni.data0 = (long)0;		// Hash array index (like m_FindIndex)
		fni.data1 = (long)NULL;		// Node pointer (like m_FindNode)
	}

	CFrameLinkNode* FindNode = (CFrameLinkNode*)(fni.data1);
	if (FindNode != NULL) FindNode = FindNode->m_Next;

	while ((FindNode == NULL) && (fni.data0 < m_TableSize)) {
		FindNode = m_HashTable[fni.data0++];
	}
	fni.data1 = (long)FindNode;

	if (FindNode == NULL) {
		fni.info = FIND_NEXT_FINISHED;
	}
	else {
		key = FindNode->m_Key;
		value = FindNode->m_Value;
	}
}

CFrameImpl::CFrameLinkNode* CFrameImpl::GetNodePtr(const TString& key)
{
	int idx = HashFunction(key);

	CFrameLinkNode* node = m_HashTable[idx];
	while (node != NULL) {
		if (node->m_Key.CompareNoCase(key) == 0) {
			break;
		}
		node = node->m_Next;
	}
	return node;
}

//=========================================================

CClassImpl::CClassImpl(VALUE base, TString className)
{
	ASSERT(!className.IsEmpty());

	m_bIsClassInstance = false;
	m_BaseClass = base;
	m_ClassName = className;
	m_MemberSymTab = AllocFrame(19);
	if (base.Type() != stUndefined) {
		m_MemberSymTab.Set(_T("inherited"), base);
	}
	else {
		m_MemberSymTab.Set(_T("inherited"), AllocNil());
	}
}

CClassImpl::~CClassImpl(){}

void CClassImpl::__del__(VALUE* self){}

bool CClassImpl::IsA(TString className)
{
	if (className.CompareNoCase(m_ClassName) == 0) return true;
	else if (m_BaseClass.Type() == stClass) {
		return m_BaseClass.Class()->IsA(className);
	}
	return false;
}

VALUE CClassImpl::operator[] (const VALUE& arg)
{
	return __get__(arg);
}

VALUE CClassImpl::__get__(const VALUE& arg)
{
	if (arg.Type() == stString) {
		VALUE* pVal = FindValuePtr(arg, false);
		if (pVal != NULL) return *pVal;
		else return VALUE();
	}
	else {
		RunTimeError(_T("Class members must be refered to with identifiers."));
		return VALUE();
	}
}

void CClassImpl::__set__(const VALUE& indexer, const VALUE& arg)
{
	if (indexer.Type() == stString) {
		m_MemberSymTab.Set(indexer, arg);
	}
	else {
		RunTimeError(_T("Index to a class must be a string value."));
	}
}

VALUE* CClassImpl::FindValuePtr(const VALUE& key, bool bCreateOK)
{
	if (key.Type() == stString) {
		VALUE* pVal = m_MemberSymTab.FindValuePtr(key, false);
		if ((pVal == NULL) && (m_BaseClass.Type() != stUndefined)) {
			return m_BaseClass.FindValuePtr(key, false);
		}

		if (pVal != NULL) {
			if (pVal->Type() == stFunction) {
				pVal->Function()->m_pClassImpl = this;
				pVal->Function()->m_pClassInstImpl = NULL;
			}
			else if (pVal->Type() == stProperty) {
				if (pVal->Property()->m_ReadFunc.Type() == stFunction) {
					pVal->Property()->m_ReadFunc.Function()->m_pClassImpl = this;
					pVal->Property()->m_ReadFunc.Function()->m_pClassInstImpl = NULL;
				}
				if (pVal->Property()->m_WriteFunc.Type() == stFunction) {
					pVal->Property()->m_WriteFunc.Function()->m_pClassImpl = this;
					pVal->Property()->m_WriteFunc.Function()->m_pClassInstImpl = NULL;
				}
				if (pVal->Property()->m_vPropValue.Type() == stFunction) {
					pVal->Property()->m_vPropValue.Function()->m_pClassImpl = this;
					pVal->Property()->m_vPropValue.Function()->m_pClassInstImpl = NULL;
				}
			}
		}
		return pVal;
	}
	else InternalError(_T("Invalid arg type sent to CClassImpl::FindValuePtr()"));
	return NULL;
}

VALUE* CClassImpl::FindValuePtrNoSelf(TString& key)
{
	// There is no self in the CClassImpl, so just call the original
	VALUE vKey(stString, key);
	return FindValuePtr(vKey, false);
}

TString CClassImpl::__unparse__()
{
	TString temp;
	temp.Format(_T("<class=%s>"), m_ClassName);
	return temp;
}

TString CClassImpl::__string__()
{
	TString temp;
	temp.Format(_T("<class=%s>"), m_ClassName);
	return temp;
}

//============
// First the constructor, and then __init__ is called if it exists

CClassInstImpl::CClassInstImpl(VALUE base, CInterpreter* pCreater)
:CClassImpl(base, base.Class()->m_ClassName)
{
	m_bIsClassInstance = true;

	if (base.Type() == stUndefined) {
		ASSERT(false);
		InternalError(_T("Undefined base sent to CClassInstImpl constructor."));
	}
	m_SelfSymTab = AllocFrame(29);
	m_SelfSymTab.Set(_T("#pClassInst"), (int)this);
	m_SelfSymTab.Set(_T("#pInterp"), (int)pCreater);
	//m_SelfSymTab.Set(_T("__base__"), base.Class()->m_ClassName);

	// This is a little screwy.  I'm not sure if I'd do it this way again,
	// but since CClassInstImpl is derived from CClassImpl, the m_BaseClass
	// variable sort of has to shades of meaning.  For a class instance,
	// when we call tokInherited, what we want is m_BaseClass->m_BaseClass,
	// because in a class instance, m_BaseClass is the actuall class object
	// that this is an instance of.  So, we have to reset inherited...
	if (base.Type() == stClass) {
		m_MemberSymTab.Set(_T("inherited"), base.Class()->m_BaseClass);
	}
	else {
		m_MemberSymTab.Set(_T("inherited"), AllocNil());
	}
}

CClassInstImpl::~CClassInstImpl()
{
	// A class instances m_BaseClass should never be stUndefined
	ASSERT(m_BaseClass.Type() == stClass);

	// delInterp is a hack to keep CClassInstImpl destructor from crashing
	// if it is called from outside the interpreter. Which could happen
	// if a class is instantiated in an external context
	// Currently CClassInstImpl's m_SelfSymTab contains a pointer to the interpeter
	// that created it and they should use that, except it might have been deleted.
	// Doing things this way means the class destructor better not reference
	// anything that isn't a member of itself (no modules will exist in delInterp).
	//CInterpreter delInterp;
	CInterpreter* delInterp = (CInterpreter*)m_SelfSymTab.Get(_T("#pInterp")).Integer();
	ASSERT(delInterp != NULL);
	if (delInterp == NULL) {
		//OutputDebugString(_T("*** Null interpreter pointer ~CClassInstImpl() ***\n"));
		return;
	}

	// It's possible these are set, store them so we can replace them
	// before leaving.  Otherwise the ExecFunc stuff in here will trigger
	// mulitple messages...
	bool bException = delInterp->GetReturnFlag() == RETURN_EXCEPTION;
	VALUE vException = delInterp->GetException();

	// We set m_RefCount to a large number (actually anything above one should do)
	// because otherwise, when the VALUE forcedClassInst is destructed from the
	// stack, it would attempt to call this class instances C++ destructer again.
	// i.e. this function we are in now, causing an infinite loop.
	VALUE forcedClassInst;
	forcedClassInst.ForceCI(stClassInstance, this);
	m_RefCount = 100;	// Semi-agregious hack

	VALUE* pVal;

	pVal = FindValuePtrNoSelf(TString(_T("__del__")));
	if ((pVal != NULL) && (pVal->Type() == stFunction)) {
		if (!delInterp->ExecFuncFromCore(VALUE(), *pVal, 0, NULL, &forcedClassInst)) {
			if (!bException) {
				VALUE vMsg = delInterp->GetException().Get(_T("msg"));
				if (vMsg.Type() != stUndefined) {
					//MessageBox(NULL, vMsg.String(), _T("Exception in class destructor"), MB_OK);
				}
			}
		}
	}

	// We need to loop down all inherited __del__ funcs...
	if ((m_BaseClass.Type() == stClass) && (m_BaseClass.Class()->m_BaseClass.Type() == stClass)) {
		//VALUE vInherited = m_MemberSymTab.Get(_T("inherited"));
		VALUE vInherited = m_BaseClass.Class()->m_BaseClass.Class()->m_BaseClass;
		while (vInherited.Type() == stClass) {
			pVal = vInherited.Class()->FindValuePtr(_T("__del__"), false);
			if ((pVal != NULL) && (pVal->Type() == stFunction)) {
				if (!delInterp->ExecFuncFromCore(VALUE(), *pVal, 0, NULL, &forcedClassInst)) {
					if (!bException) {
						VALUE vMsg = delInterp->GetException().Get(_T("msg"));
						if (vMsg.Type() != stUndefined) {
							//MessageBox(NULL, vMsg.String(), _T("Exception in class destructor"), MB_OK);
						}
					}
				}
			}
			//vInherited = vInherited.Class()->m_MemberSymTab.Get(_T("inherited"));
			vInherited = vInherited.Class()->m_BaseClass;
		}
	}

	if (bException) delInterp->SetExceptionInstance(vException); // Restore original exception
}

VALUE* CClassInstImpl::FindValuePtr(const VALUE& key, bool bCreateOK)
{
	if (key.Type() == stString) {
		CClassImpl* pClassImpl = NULL;

		VALUE* pVal = m_SelfSymTab.FindValuePtr(key, false);
		if (pVal == NULL) {
			pVal = CClassImpl::FindValuePtr(key, false);	// Search base classes
		}
		else {
			pClassImpl = m_BaseClass.Class();
		}

		if ((pVal == NULL) && bCreateOK) {
			// We need to create the value now
			pVal = m_SelfSymTab.FindValuePtr(key, true);
		}

		if (pVal != NULL) {
			// If a function came from a base class, then m_pClassImpl will
			// already be set to the class it came from, so don't change it
			if (pVal->Type() == stFunction) {
				if (pClassImpl)
					pVal->Function()->m_pClassImpl = pClassImpl;
				pVal->Function()->m_pClassInstImpl = this;
			}
			else if (pVal->Type() == stProperty) {
				if (pVal->Property()->m_ReadFunc.Type() == stFunction) {
					if (pClassImpl)
						pVal->Property()->m_ReadFunc.Function()->m_pClassImpl = pClassImpl;
					pVal->Property()->m_ReadFunc.Function()->m_pClassInstImpl = this;
				}
				if (pVal->Property()->m_WriteFunc.Type() == stFunction) {
					if (pClassImpl)
						pVal->Property()->m_WriteFunc.Function()->m_pClassImpl = pClassImpl;
					pVal->Property()->m_WriteFunc.Function()->m_pClassInstImpl = this;
				}
				if (pVal->Property()->m_vPropValue.Type() == stFunction) {
					if (pClassImpl)
						pVal->Property()->m_vPropValue.Function()->m_pClassImpl = pClassImpl;
					pVal->Property()->m_vPropValue.Function()->m_pClassInstImpl = this;
				}
			}
		}
		return pVal;
	}
	InternalError(_T("Invalid arg type sent to CClassInstImpl::FindValuePtr()"));
	return NULL;
}

VALUE* CClassInstImpl::FindValuePtrNoSelf(TString& key)
{
	VALUE* pVal = CClassImpl::FindValuePtr(key, false);	// Search base classes

	if (pVal != NULL) {
		// If a function, then m_pClassImpl will already be set
		// by CClassImpl::FindValuePtr(), don't change it here
		if (pVal->Type() == stFunction) {
			pVal->Function()->m_pClassInstImpl = this;
		}
		else if (pVal->Type() == stProperty) {
			if (pVal->Property()->m_ReadFunc.Type() == stFunction) {
				pVal->Property()->m_ReadFunc.Function()->m_pClassInstImpl = this;
			}
			if (pVal->Property()->m_WriteFunc.Type() == stFunction) {
				pVal->Property()->m_WriteFunc.Function()->m_pClassInstImpl = this;
			}
			if (pVal->Property()->m_vPropValue.Type() == stFunction) {
				pVal->Property()->m_vPropValue.Function()->m_pClassInstImpl = this;
			}
		}
	}
	return pVal;
}

VALUE CClassInstImpl::operator[] (const VALUE& arg)
{
	return __get__(arg);
}

VALUE CClassInstImpl::__get__(const VALUE& arg)
{
	if (arg.Type() == stString) {
		VALUE* pVal = FindValuePtr(arg, false);
		if (pVal != NULL) return *pVal;
		else return VALUE();
	}
	else {
		RunTimeError(_T("Class instance members must be refered to with identifiers."));
		return VALUE();
	}
}

void CClassInstImpl::__set__(const VALUE& indexer, const VALUE& arg)
{
	if (indexer.Type() == stString) {
		m_SelfSymTab.Set(indexer, arg);
	}
	else {
		RunTimeError(_T("Index to a class instance must be a string value."));
	}
}

TString CClassInstImpl::__unparse__()
{
	return _T("<classinstance=") + m_BaseClass.Class()->m_ClassName + _T(">");
}

TString CClassInstImpl::__string__()
{
	return m_SelfSymTab.String();
}

//=========================================================
